"""@plugin CRIMEVulnerabilityTesterPlugin
This file contains classes for plugin, which tests server(s) for vulnerability CVE-2012-4929.

@author: Bc. Pavel Soukup
"""


from operator import attrgetter
from xml.etree.ElementTree import Element

from sets import Set

from sslyze.plugins.common_new_plugin_info import PROTOCOL_VERSION
from sslyze.plugins import plugin_base
from sslyze.plugins.plugin_base import PluginResult
from sslyze.utils.ssl_connection import SSLHandshakeRejected, SSLConnection
from sslyze.utils.thread_pool import ThreadPool


class CRIMEVulnerabilityTesterPlugin(plugin_base.PluginBase):
    """class CRIMEVulnerabilityTesterPlugin
    
    This class inherited from abstract class PluginBase. Instance of this class tests server for vulnerability CVE-2012-4929 and makes decision if the server is vulnerable.
    """
    interface = plugin_base.PluginInterface(
        "CRIMEVulnerabilityTesterPlugin",
        "Scans the server(s) and checks if requirements for CRIME attack are satisfied.")
    interface.add_command(
        command="crime",
        help="Tests server for CVE-2012-4929 vulnerability.")

    VERBOSE_LINE = '  Compression for {protocol:<10}{status:<20}'.format

    def process_task(self, server_connectivity_info, plugin_command, options_dict=None):
        if options_dict and 'verbose' in options_dict.keys():
            verbose_mode = options_dict['verbose']
        else:
            verbose_mode = False
        is_vulnerable = False
        protocols = {'SSLv3', 'SSLv2', 'TLSv1', 'TLSv1.1', 'TLSv1.2'}
        if verbose_mode:
            print '  VERBOSE MODE PRINT'
            print '  ------------------'
        for proto in protocols:
            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(override_ssl_version=PROTOCOL_VERSION[proto])
            try:
                ssl_connection.connect()
                compression = True if ssl_connection.get_current_compression_method() is not None else False
                is_vulnerable |= compression
            except SSLHandshakeRejected as e:
                if verbose_mode:
                    print self.VERBOSE_LINE(protocol=proto, status='Connect error: {error}'.format(error=str(e)))
                pass
            else:
                if verbose_mode:
                    print self.VERBOSE_LINE(protocol=proto, status=str(compression))
            finally:
                ssl_connection.close()
        if verbose_mode:
            print '  ----------------------'
            print '  END VERBOSE MODE PRINT'
            print '  ----------------------'

        return CRIMEVulnerabilityTesterResult(server_connectivity_info, plugin_command, options_dict, is_vulnerable)

class CRIMEVulnerabilityTesterResult(PluginResult):
    """class CRIMEVulnerabilityTesterResult
    
    This class is subclass PluginResult. It's used to return result of test, which is made in class CRIMEVulnerabilityTesterPlugin.
    """
    def __init__(self, server_info, plugin_command, plugin_options, is_vulnerable):
        super(CRIMEVulnerabilityTesterResult,self).__init__(server_info, plugin_command, plugin_options)
        self.is_vulnerable = is_vulnerable

    COMMAND_TITLE = 'Vulnerability CVE-2012-4929'
    def as_text(self):
        crime_txt = 'VULNERABLE - Server is vulnerable to CRIME attack' if self.is_vulnerable \
            else 'OK - NOT VULNERABLE to CRIME attack'
        output_txt = [self.PLUGIN_TITLE_FORMAT(self.COMMAND_TITLE)]
        output_txt.append(self.FIELD_FORMAT("",crime_txt))
        return output_txt

    def as_xml(self):
        xml_ouput = Element(self.plugin_command, title=self.COMMAND_TITLE)
        xml_ouput.append(Element('vulnerable', isVulnerable=str(self.is_vulnerable)))
        return xml_ouput